﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Pixysoft.Tools;
using System.Data;

namespace Pixysoft.Framework.Noebe.Core
{
    class TransactionProxyConnection : AbstractProxyConnection, IProxyConnection
    {
        private ConnectionPerformance performance = new ConnectionPerformance();

        private TransactionConnectionPool pool = null;

        public TransactionProxyConnection(String connectionId, IDbConnection connection, TransactionConnectionPool pool)
            : base(connectionId, connection)
        {
            this.pool = pool;

        }

        public override void Close()
        {
            EndRequest();

            pool.RemovePendingConnection(this);

            if (!pool.IsOverAvailable(this.performance.GetRequestInterval(), this.performance.GetProcessInterval()))
            {
                pool.PutAvailableConnection(this);
            }
            else
            {
                LoggerHelper.Info("connection pool is over available, connection is removed when Close, would Dispose, id = " + this.ConnectionId);

                pool.ThreadSafeRemoveAvailableConnection(this);

                this.ReallyClose();

                this.ReallyDispose();
            }
        }

        public override void Dispose()
        {
            //just do nothing.
        }

        public int GetRequestTimes()
        {
            return this.performance.GetRequestTimes();
        }

        public ConnectionSnapshot CreateShapshot()
        {
            ConnectionSnapshot admin = new ConnectionSnapshot();
            admin.ConnectionId = this.ConnectionId;
            admin.CreateDate = this.performance.GetCreateDate();
            admin.RequestCount = this.performance.GetRequestCount();
            admin.RequestTimes = this.performance.GetRequestTimes();
            return admin;
        }

        public void ReallyClose()
        {
            base.Close();
        }

        public void ReallyDispose()
        {
            if (IsDisposed())
                return;

            base.Dispose();
            this.pool = null;
            this.performance = null;
        }

        public bool TryDoRequest()
        {
            if (this.IsDisposed())
                return false;

            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                this.performance.BeginRequest();

                return true;
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        public void CancelRequest()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                this.performance.CancelRequest();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        public void EndRequest()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                this.performance.EndRequest();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        public int GetStatementSize()
        {
            locker.AcquireReaderLock(Timeout.Infinite);

            try
            {
                return this.performance.GetRequestCount();
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
        }

        public bool IsTimeout()
        {
            return this.performance.IsTimeout();
        }

        //NON THREAD SAFE
        class ConnectionPerformance
        {
            //total request times
            int requestTimes = 0;
            //processing request count
            int requestCount = 0;

            double requestInterval = -1;

            double processInterval = -1;

            DateTime createDate = new DateTime();

            DateTime currentRequestTime = DateTime.MinValue;

            DateTime startRequestTime = DateTime.MinValue;

            public ConnectionPerformance()
            {
                this.createDate = GlobalTimer.Instance.GetGlobalTime();
            }

            /* return request interval */
            public double BeginRequest()
            {
                this.requestCount++;

                this.requestTimes++;

                DateTime requestTime = GlobalTimer.Instance.GetGlobalTime();

                this.startRequestTime = requestTime;

                if (this.currentRequestTime == DateTime.MinValue)
                {
                    this.currentRequestTime = requestTime;

                    return -1;
                }

                TimeSpan interval = requestTime - this.currentRequestTime;

                currentRequestTime = requestTime;

                if (this.requestInterval < 0)
                    this.requestInterval = interval.TotalMilliseconds;

                double nRequestInterval = (this.requestInterval * this.requestTimes + interval.TotalMilliseconds) / (this.requestTimes + 1);

                this.requestInterval = nRequestInterval;

                return this.requestInterval;
            }

            /* cancel the request */
            public void CancelRequest()
            {
                this.requestCount--;

                this.requestTimes--;
            }

            /* return process interval */
            public double EndRequest()
            {
                requestCount--;

                DateTime requestTime = GlobalTimer.Instance.GetGlobalTime();

                DateTime currentMsecs = this.startRequestTime;

                TimeSpan interval = requestTime - currentMsecs;

                currentRequestTime = requestTime;

                if (this.processInterval < 0)
                    this.processInterval = interval.TotalMilliseconds;

                double nRequestAverageSpan = (this.processInterval * this.requestTimes + interval.TotalMilliseconds) / (this.requestTimes + 1);

                this.processInterval = nRequestAverageSpan;

                return this.processInterval;
            }

            public bool IsTimeout()
            {
                if (currentRequestTime == DateTime.MinValue)
                    return false;

                return currentRequestTime < GlobalTimer.Instance.GetGlobalTime().AddMilliseconds(StringHelper.timeout_connection * -1);
            }

            public double GetRequestInterval()
            {
                return requestInterval;
            }

            public double GetProcessInterval()
            {
                return processInterval;
            }

            public int GetRequestTimes()
            {
                return requestTimes;
            }

            public int GetRequestCount()
            {
                return this.requestCount;
            }

            public DateTime GetCreateDate()
            {
                return createDate;
            }

            public void Dispose()
            {

            }
        }
    }
}